/**
 * Software License, Version 1.0
 *
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 *
 *
 *Redistribution and use in source and binary forms, with or without
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For
 * further information contact the Community Grids Lab at
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and
 * wherever such third-party acknowledgments normally appear.
 *
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering,
 * shall not be used to endorse or promote products derived from this software
 * without prior written permission from Indiana University.  For written
 * permission, please contact the Advanced Research and Technology Institute
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering,
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 *
 *
 * Indiana University provides no reassurances that the source code provided
 * does not infringe the patent or any other intellectual property rights of
 * any other entity.  Indiana University disclaims any liability to any
 * recipient for claims brought by any other entity based on infringement of
 * intellectual property rights or otherwise.
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS.
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS",
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION
 *GENERATED USING SOFTWARE.
 */
package cgl.narada.matching.rtp;

import cgl.narada.protocol.Protocol;
import cgl.narada.util.ByteUtilities;

/**
  This RtpEvent class serializes and deserializes RtpEvent objects

  Here is the format of serialization:
  Byte 0    = either Protocol.NARADA_RTP_EVENT or Protocol.NARADA_RTP_CLIENT_EVENT
  byte 1    = media type
  Bytes 2-5 = an int, which is the source id
  Bytes 6-13 = an int, which is the topic number
  Bytes 14-end = data

  @author Ahmet Uyar, Shrideep Pallickara
  $Date$
  $Revision$
 */

public class RtpEvent {
  private int source;
  private long topicNumber;
  /* This holds only the data. there is no other data in this array.
     data will not be initialized unless the getData method is called*/
  private byte[] data = null;

  /* There might be some other data in addition to RtpEvent data, so in this
     case offset shows the start point of real data and length shows the
     length of the data*/
  private byte[] buffer;
  private int offset;
  private int length;
  private String moduleName = "RtpEvent: ";

  // this shows the type of RtpEvent. it can be audio, video, rtcp, etc.
  private byte type = 0;

  public static final byte AUDIO_EVENT = 0;
  public static final byte VIDEO_EVENT = 1;
  public static final byte RTCP_EVENT = 2;
  public static final byte NO_TYPE_EVENT = 3; // this is to support for previous versions

  /**
   * construct an event with the given data
   * given byte array only has the data.
   * it means the offset is zero and the length of data is the length of the array.
   * the type of this event is NO_TYPE_EVENT
   */
  public RtpEvent(int source, long topicNumber, byte[] data) {
     this(source, topicNumber, data, 0, data.length, NO_TYPE_EVENT);
  }

  /**
   * construct an event with the given parameters
   * the type of this event is NO_TYPE_EVENT
   */
  public RtpEvent(int source, long topicNumber, byte[] buffer, int offset, int length) {
     this(source, topicNumber, buffer, offset, length, NO_TYPE_EVENT);
  }

  /**
   * construct an event with the given parameters.
   * @param source source id
   * @param topicNumber topic number
   * @param buffer byte array.
   * @param offset the start point of the data in the buffer array
   * @param length the length of the data in the buffer
   * @param type the type of this event
   */
  public RtpEvent(int source,
                  long topicNumber,
                  byte[] buffer,
                  int offset,
                  int length,
                  byte type) {

    this.source = source;
    this.topicNumber = topicNumber;
    this.buffer = buffer;
    this.offset = offset;
    this.length = length;
    this.type = type;
  }

  public String toString() {
    String stringRep = "Topic=" + topicNumber + ", source=" + source;
    return stringRep;
  }

  /**
   * This method constructs a new RtpEvent object from a byte array.
   * byte array must be constructed with one of the getbytes method of this
   * class.
   */
  public RtpEvent(byte[] rtpEventBytes) {
    // Ignore the first byte since it has Narada flag

    // second byte is RtpEvent type
    type = rtpEventBytes[1];

    // bytes 2-5 are source id
    source = ByteUtilities.getInt(rtpEventBytes, 2);

    // bytes 6-9 are topic number
    topicNumber = ByteUtilities.getLong(rtpEventBytes, 6);
    offset = 1 +1 + 4 + 8;
    length = rtpEventBytes.length - offset;
    buffer = rtpEventBytes;
  }

  /**
   * @return the type of this event
   */
  public byte getType(){
     return type;
  }

  /**
   * @return true if this is an audio event
   */
  public boolean isAudio(){
     if(type == AUDIO_EVENT)
        return true;

     return false;
  }

  public long getTopic() {
    return topicNumber;
  }

  /**
   * return the buffer array which might have both data and some other info,
   * so it should be used with offset and length to access the real data.
   */
  public byte[] getBuffer(){
    return buffer;
  }

  /**
   * returns a byte array which only has the data
   */
  public byte[] getData() {

    if(data != null)
      return data;

    data = new byte[length];
    System.arraycopy(buffer, offset, data, 0, length);
    return data;
  }

  public int getSource() {
    return source;
  }

  public int getOffset(){
    return offset;
  }

  public int getDataLength(){
    return length;
  }

  /**
   * return serialized event. It will has Protocol.NARADA_RTP_EVENT flag.
   */
  public byte[] getBytes() {
    return getBytes(Protocol.NARADA_RTP_EVENT);
  }

  /**
     Serializes the RtpEvent object with the Protocol.NARADA_RTP_CLIENT_EVENT
     as first byte
  */
  public byte[] getBytesWithClientFlag() {
    return getBytes(Protocol.NARADA_RTP_CLIENT_EVENT );
  }

  /**
   * return serialized event with the given flag.
   * It can be either one of the following flags:
   *    Protocol.NARADA_RTP_EVENT
   *    Protocol.NARADA_RTP_CLIENT_EVENT
   */
  public byte[] getBytes(byte flag) {

    if ( buffer == null ) {
      System.out.println(moduleName +
                         "Data associated with rtp packet is null");
      return null;
    }

    byte[] eventBytes = new byte[1 +1 + 4 + 8 + length];
    eventBytes[0] = flag;
    return getBytes(eventBytes, 1);
  }

  /**
     Encodes this RtpEvent to a byte array so that we can send it through the
     network to other brokers and clients real data starts from startPoint

     @param byteArray byte array to put the result
     @return returns true if it succeeds, otherwise false
  */
  private byte[] getBytes(byte[] eventBytes, int startPoint) {

    int counter = startPoint;
    try {

       // encode the RtpEvent type
       eventBytes[counter++] = type;

      //encode sourceID
      byte[] bytes = ByteUtilities.getBytes(source);
      eventBytes[counter++] = bytes[0];
      eventBytes[counter++] = bytes[1];
      eventBytes[counter++] = bytes[2];
      eventBytes[counter++] = bytes[3];

      //encode topicID
      bytes = ByteUtilities.getBytes(topicNumber);
      eventBytes[counter++] = bytes[0];
      eventBytes[counter++] = bytes[1];
      eventBytes[counter++] = bytes[2];
      eventBytes[counter++] = bytes[3];
      eventBytes[counter++] = bytes[4];
      eventBytes[counter++] = bytes[5];
      eventBytes[counter++] = bytes[6];
      eventBytes[counter++] = bytes[7];

      //encode data
      for (int i = offset; i < (offset + length); i++) {
        eventBytes[counter++] = buffer[i];
      }

    } catch (Exception e) {
      System.out.println("RTPEvent: Error marshalling stream" + e);
    } // end try-catch

    return eventBytes;
  }

  /**
   * check whether the given data array has a serialized audio rtp event
   * @return
   */
  public static boolean isAudioEvent(byte data[]){

     if(data == null || data.length < 10)
        return false;

     // first check whether this is an RtpEvent
     // then check whether this in an Audio Event
     if ( (data[0] == Protocol.NARADA_RTP_EVENT ||
           data[0] == Protocol.NARADA_RTP_CLIENT_EVENT)
          && data[1] == AUDIO_EVENT)
        return true;

     return false;
  }

  public static void main(String[] args) {
    String dataStr = "This is a test .... ";
    byte[] data = dataStr.getBytes();
    System.out.println("data length: "+data.length);

    RtpEvent rtpEvt1;
    RtpEvent rtpEvt2;
    RtpEvent rtpEvt3, rtpEvt4;
    rtpEvt1 = new RtpEvent(1002, 1113, data);
    rtpEvt2 = new RtpEvent(rtpEvt1.getBytesWithClientFlag());
    rtpEvt3 = new RtpEvent(rtpEvt2.getBytes());
    rtpEvt4 = new RtpEvent(rtpEvt3.getBytes());

    System.out.println(rtpEvt1 + "\n" + rtpEvt2 + "\n" + rtpEvt3 + "\n" +
                       rtpEvt4);

    System.out.println(rtpEvt1.getTopic() + ", length=" +
                       rtpEvt1.getData().length + "::" + rtpEvt1.getSource());
    System.out.println(rtpEvt2.getTopic() + ", length=" +
                       rtpEvt2.getData().length + "::" + rtpEvt2.getSource());
    System.out.println(rtpEvt3.getTopic() + ", length=" +
                       rtpEvt3.getDataLength() + "::" + rtpEvt3.getSource());

    System.out.println("serialized length: "+rtpEvt1.getBytes().length);

  }
}




